﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using System.Timers;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using System.IO;

namespace dasFX.VSTime.API
{
    public class VSTimeExceptionEvent
    {
        public Exception Exception;
    }

    public class VSTimeEventHandler
    {
        private IEventRecorder _recorder;
        private EnvDTE.DTE _dte;
        private OutputWindowPane _outputWindowPane;
        private EnvDTE.Events _events;
        private WindowEvents _winEvents;
        private DocumentEvents _docEvents;
        private BuildEvents _buildEvents;
        private DebuggerEvents _debugEvents;
        private TextEditorEvents _textEvents;
        SolutionEvents _solutionEvents;

        

        #region Constructors
        public VSTimeEventHandler(EnvDTE.DTE dte)
        {
            try
            {
                ISubmitEvent submitter = new SQLSubmitEvent(string.Empty, new SolutionRepository(), new EventRecordRepository());
                _dte = dte;
                RegisterOutputWindow();
                RegisterDTEEVents();
                _recorder = new EventRecorder(submitter, _outputWindowPane);
                submitter.RegisterSolution(_dte.Solution.FullName);
                _outputWindowPane.OutputString("Opened Solution: " + _dte.Solution.FileName + "\n");
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }
        #endregion

        #region Public Methods
        public delegate void OnExceptionEvent(Exception ex);
        public event OnExceptionEvent ExceptionEvent;

        public delegate void OnSolutionOpened(int SolutionID);
        public event OnSolutionOpened SolutionOpenedEvent;
        #endregion

        #region Private Methods
        void RegisterOutputWindow()
        {
            OutputWindow outputWindow = (OutputWindow)_dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object;
            _outputWindowPane = outputWindow.OutputWindowPanes.Add("DTE Event Information");
        }

        public void RegisterDTEEVents()
        {
            _events = _dte.Events;
            _winEvents = _events.get_WindowEvents(null);
            _docEvents = _events.get_DocumentEvents(null);
            _buildEvents = _events.BuildEvents;
            _debugEvents = _events.DebuggerEvents;
            _solutionEvents = _events.SolutionEvents;

            _events.DTEEvents.OnBeginShutdown += new _dispDTEEvents_OnBeginShutdownEventHandler(DTEEvents_OnBeginShutdown);

            //Solution Events
            _solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(_solutionEvents_Opened);
            _solutionEvents.BeforeClosing += new _dispSolutionEvents_BeforeClosingEventHandler(_solutionEvents_BeforeClosing);

            //Window Events
            _winEvents.WindowActivated += new _dispWindowEvents_WindowActivatedEventHandler(_winEvents_WindowActivated);
            _winEvents.WindowClosing += new _dispWindowEvents_WindowClosingEventHandler(_winEvents_WindowClosing);
            _winEvents.WindowCreated += new _dispWindowEvents_WindowCreatedEventHandler(_winEvents_WindowCreated);
            //Build Events
            _buildEvents.OnBuildBegin += new _dispBuildEvents_OnBuildBeginEventHandler(_buildEvents_OnBuildBegin);
            _buildEvents.OnBuildDone += new _dispBuildEvents_OnBuildDoneEventHandler(_buildEvents_OnBuildDone);
            //Document Events
            _docEvents.DocumentOpening += new _dispDocumentEvents_DocumentOpeningEventHandler(_docEvents_DocumentOpening);
            //Debug Events
            _debugEvents.OnEnterRunMode += new _dispDebuggerEvents_OnEnterRunModeEventHandler(_debugEvents_OnEnterRunMode);
            _debugEvents.OnExceptionThrown += new _dispDebuggerEvents_OnExceptionThrownEventHandler(_debugEvents_OnExceptionThrown);
            _debugEvents.OnEnterDesignMode += new _dispDebuggerEvents_OnEnterDesignModeEventHandler(_debugEvents_OnEnterDesignMode);
            //Text Events
            _textEvents = _events.get_TextEditorEvents(null);
            _textEvents.LineChanged += new _dispTextEditorEvents_LineChangedEventHandler(_textEvents_LineChanged);
        }

        void _solutionEvents_BeforeClosing()
        {
            try
            { _recorder.Solution_BeforeClosing(_dte.Solution.FileName); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _solutionEvents_Opened()
        {
            try
            {
                string solutionFile = Path.GetFileName(_dte.Solution.FileName);
                //Record the open, will register the solution
                _recorder.Solution_Opened(solutionFile);
                //Get the solutionID
                Solution s = new SolutionRepository().DoQuery("Solution").Where(x => x.SolutionFileName == solutionFile).FirstOrDefault();
                if (s != null)
                {
                    SolutionOpened(s.ID);
                }
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        /// <summary>
        /// Functions as a keep-alive, preventing the Idled event from registering while
        /// the developer works in on a file
        /// </summary>
        void _textEvents_LineChanged(TextPoint StartPoint, TextPoint EndPoint, int Hint)
        {
            try
            { _recorder.Text_LineChanged(_dte.ActiveDocument.Name); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _debugEvents_OnEnterDesignMode(dbgEventReason Reason)
        {
            try
            { _recorder.Debug_OnEnterDesignMode(Enum.GetName(typeof(dbgEventReason), Reason)); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _debugEvents_OnExceptionThrown(string ExceptionType, string Name, int Code, string Description, ref dbgExceptionAction ExceptionAction)
        {
            try
            { _recorder.Debug_OnExceptionThrown(Description); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _debugEvents_OnEnterRunMode(dbgEventReason Reason)
        {
            try
            { _recorder.Debug_OnEnterRunMode(Reason.ToString()); }
            catch (Exception ex)
            {
                HandleException(ex);
            }  
        }

        void _buildEvents_OnBuildDone(vsBuildScope Scope, vsBuildAction Action)
        {
            try
            { _recorder.Build_OnBuildDone(Action.ToString()); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _buildEvents_OnBuildBegin(vsBuildScope Scope, vsBuildAction Action)
        {
            try
            { _recorder.Build_OnBuildBegin(Action.ToString()); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void DTEEvents_OnBeginShutdown()
        {
            try
            { _recorder.DTEEvents_OnBeginShutdown(); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _winEvents_WindowCreated(Window Window)
        {
            try
            { _recorder.Window_WindowCreated(Window.Caption); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _docEvents_DocumentOpening(string DocumentPath, bool ReadOnly)
        {
            try
            { _recorder.Document_DocumentOpening(DocumentPath); }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _winEvents_WindowClosing(Window Window)
        {
            try
            { 
                _recorder.Window_WindowClosing(Window.Caption); 
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void _winEvents_WindowActivated(Window GotFocus, Window LostFocus)
        {
            try
            {
                if (GotFocus.Document != null)
                { _recorder.Window_WindowActivated(Path.GetFileName(GotFocus.Document.FullName)); }
                else
                { _recorder.Window_WindowActivated(GotFocus.Caption); }
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void HandleException(Exception ex)
        {
            VSTimeExceptionHandler.ReportException(ex);
            if (ExceptionEvent != null)
            {
                ExceptionEvent(ex);
            }
        }

        void SolutionOpened(int SolutionID)
        {
            if (SolutionOpenedEvent != null)
            {
                SolutionOpenedEvent(SolutionID);
            }
        }
        #endregion
    }
}
